using System;
using LiteTools.constants.enums;
using LiteTools.constants.exceptions;

namespace LiteTools.tools
{
    public class TlvUtil
    {
        /// <summary>
        /// 将一个tlv的数据中的length值转换成一个十进制的长度值
        /// </summary>
        /// <param name="length"> TLV中的长度值，比如7F，或者8180 </param>
        /// <returns> 转换完成的TLV的长度，比如7F转换成255 </returns>
        public static string GetDecLength(string length)
        {
            var head = length.Substring(0, 2);
            switch (head)
            {
                case "81":
                    return Convert.ToInt32(length.Substring(2, 2), 16).ToString();
                case "82":
                    return Convert.ToInt32(length.Substring(2, 4), 16).ToString();
                case "83":
                    return Convert.ToInt32(length.Substring(2, 6), 16).ToString();
                default:
                    return Convert.ToInt32(head, 16).ToString();
            }
        }

        public static string GetLengthHex(string length)
        {
            var len = Convert.ToInt32(length, 16);
            var lengthHex = len.ToString("X");
            if (lengthHex.Length % 2 != 0)
            {
                lengthHex = "0" + lengthHex;
            }
            if (len <= 0x7F)
            {
                return lengthHex;
            }

            if (len <= 0xFF)
            {
                return "81" + lengthHex;
            }

            if (len <= 0xFFFF)
            {
                return "82" + lengthHex;
            }
            
            return "83" + lengthHex;
        }

        /// <summary>
        /// 输入TLV的一段数据获取其常速数据，如输入：81801223则返回8180，表示长度为0x80字节
        /// </summary>
        /// <returns></returns>
        public static string GetLengthValue(string context, out string contextNonLength)
        {
            int int32;
            try
            {
                int32 = Convert.ToInt32(context.Substring(0, 2), 16);
            }
            catch (Exception)
            {
                throw new NotNumberException();
            }

            if (context.Length % 2 != 0)
            {
                throw new NotSuitableDataException();
            }

            // 如长度是2，但是实际表示的数字一个字节表示不完，因此抛出数据不全的异常
            if ((context.Length == 2 && int32 > 0x7F) || (context.Length == 4 && int32 > 0x81) ||
                (context.Length == 6 && int32 > 0x82))
            {
                throw new DataNotCompletedException();
            }

            var lengthValue = context.Substring(0, 2);
            if (int32 <= 0x7F)
            {
                contextNonLength = context.Substring(2);
                return lengthValue;
            }

            switch (lengthValue)
            {
                case "81":
                    contextNonLength = context.Substring(4);
                    return context.Substring(0, 4);
                case "82":
                    contextNonLength = context.Substring(6);
                    return context.Substring(0, 6);
                default:
                    contextNonLength = context.Substring(8);
                    return context.Substring(0, 8);
            }
        }

        /// <summary>
        /// 解析给定数据中的Tag值，解析完成之后将输出去除Tag之后的新的数据
        /// </summary>
        /// <param name="context"> 原始数据 </param>
        /// <param name="contextWithoutFirstTag"> 去除解析Tag之后的数据 </param>
        /// <param name="tagType"> 标记本条数据属于什么类型的TLV </param>
        /// <param name="constructType"> 标记本条数据属于什么结构的TLV </param>
        /// <returns> Tag值 </returns>
        public static string GetTag(string context, out string contextWithoutFirstTag, out TlvClassEnum tagType,
            out TlvTypeEnum constructType)
        {
            tagType = TlvClassEnum.Normal;
            constructType = TlvTypeEnum.Primitive;
            // 先判断第一个字节
            var tagFirstByte = context.Substring(0, 2);
            var firstByte = Convert.ToInt32(tagFirstByte, 16);
            switch (firstByte & 0xC0)
            {
                case 00:
                    tagType = TlvClassEnum.Normal;
                    break;
                case 0x40:
                    tagType = TlvClassEnum.Application;
                    break;
                case 0x80:
                    tagType = TlvClassEnum.Specific;
                    break;
                case 0xC0:
                    tagType = TlvClassEnum.Private;
                    break;
            }

            if ((firstByte & 0x20) == 0x20)
            {
                constructType = TlvTypeEnum.Constructed;
            }

            if ((firstByte & 0x1F) != 0x1F)
            {
                // 此处表示后面已经没有更多字节了，那么第一个字节就是Tag
                contextWithoutFirstTag = context.Substring(2);
                return tagFirstByte;
            }

            // 如果第一个字节是0x1F就表示后续的字节还是TAG部分，那么就获取第二个字节
            var tagSecondByte = context.Substring(2, 2);
            var secondByte = Convert.ToInt32(tagSecondByte, 16);
            if ((secondByte & 0x80) == 0x00)
            {
                // 到这里表示Tag占用2字节，也就是4个字符
                contextWithoutFirstTag = context.Substring(4);
                return context.Substring(0, 4);
            }

            // 到这里表示Tag占用3字节，也就是6个字符，更多的字符的Tag这里也就不支持了
            contextWithoutFirstTag = context.Substring(6);
            return context.Substring(0, 6);
        }

        public static string GetValue(string contextNonLength, string lengthValue, out string nonValueContext)
        {
            var byteLength = int.Parse(GetDecLength(lengthValue));
            nonValueContext = contextNonLength.Substring(byteLength * 2);
            return contextNonLength.Substring(0, byteLength * 2);
        }
    }
}